package com.sunlands.zlcx.usercenter.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sunlands.zlcx.usercenter.common.annotation.AuthSig;
import com.sunlands.zlcx.usercenter.common.annotation.AutoLog;
import com.sunlands.zlcx.usercenter.common.interceptor.AutoLogAspect;
import com.sunlands.zlcx.usercenter.constant.YN;
import com.sunlands.zlcx.usercenter.constant.dic.STUDY_OPEN_DAYS;
import com.sunlands.zlcx.usercenter.constant.dic.STUDY_OPEN_DAYS_ALERT_ADVERT;
import com.sunlands.zlcx.usercenter.domain.AppSystemMessageDO;
import com.sunlands.zlcx.usercenter.domain.DicDO;
import com.sunlands.zlcx.usercenter.domain.LatestVersionDO;
import com.sunlands.zlcx.usercenter.exception.GlobalExceptionHandler;
import com.sunlands.zlcx.usercenter.service.*;
import com.sunlands.zlcx.usercenter.util.BeanPropertiesUtil;
import com.sunlands.zlcx.usercenter.util.DateUtil;
import com.sunlands.zlcx.usercenter.vo.AppErrorMessage;
import com.sunlands.zlcx.usercenter.vo.LatestVersionVO;
import com.sunlands.zlcx.usercenter.vo.OpenDayAdvertVO;
import com.sunlands.zlcx.usercenter.vo.TipVO;
import com.sunlands.zlcx.usercenter.vo.response.BusinessResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.sunlands.zlcx.usercenter.config.RedisConfig.CacheKeys.*;

/**
 * @author anjl
 * @date 1:35 PM 2019/4/15
 * description:
 */
@SuppressWarnings("unused")
@Api(tags = "1、系统信息", position = 1)
@ApiResponses(value = {@ApiResponse(code = 400, response = GlobalExceptionHandler.class, message = "数据校验失败"), @ApiResponse(code = 500, response = GlobalExceptionHandler.class, message = "内部错误")})
@Slf4j
@RestController
@RequestMapping("/system")
public class SystemController implements AuthSupport, BeanPropertiesUtil {

    @AutoLog
    @ApiOperation(value = "获取最新APP版本", notes = "获取最新APP版本")
    @GetMapping("/tips")
    @AuthSig
    public BusinessResult<TipVO> find(@RequestParam String app, @RequestParam String version) {
        if (!app.toLowerCase().equals("android") && !app.toLowerCase().equals("ios")) {
            throw new IllegalArgumentException("app类型不存在！");
        }
        Long userId = currentUserId();
        usersService.saveUserActiveLog(userId, "-");
        LatestVersionDO latestVersion = latestVersionService.find(app);
        if (latestVersion.getLatestVersion().equals(version)) {
            return BusinessResult.createSuccessInstance(null);
        }

        LatestVersionVO versionVO = LatestVersionVO.from(latestVersion);
        if (Objects.isNull(versionVO) || compareVersion(versionVO.getLatestVersion(), version) < 0) {
            return BusinessResult.createSuccessInstance(null);
        }
        versionVO.setForceUpdate(compareVersion(version, versionVO.getMinSupportVersion()) < 0);
        TipVO tip = TipVO.builder().ext(versionVO).type(TipVO.TipType.VERSION_UPDATE.name()).build();

        if (versionVO.getForceUpdate()) {
            return BusinessResult.createSuccessInstance(tip);
        }

        if (Objects.isNull(userId)) {
            return BusinessResult.createSuccessInstance(null);
        }
        Boolean hasKey = redisTemplate.hasKey(getKey(userId, app, versionVO.getLatestVersion()));
        if (hasKey) {
            String existVersion = redisTemplate.opsForValue().get(getKey(userId, app, versionVO.getLatestVersion()));
            if (StringUtils.isNotBlank(existVersion) && compareVersion(existVersion, version) < 0) {
                put(userId, app, version, versionVO.getLatestVersion());
                return BusinessResult.createSuccessInstance(tip);
            }
            return BusinessResult.createSuccessInstance(null);
        }
        put(userId, app, version, versionVO.getLatestVersion());
        return BusinessResult.createSuccessInstance(tip);
    }

    @AutoLog
    @ApiOperation(value = "考研自习室-获取最新APP版本", notes = "考研自习室-获取最新APP版本")
    @GetMapping("/postgraduate/tips")
    @AuthSig
    public BusinessResult<TipVO> version(@RequestParam String app, @RequestParam String version) {
        if (!app.toLowerCase().equals("android") && !app.toLowerCase().equals("ios")) {
            throw new IllegalArgumentException("app类型不存在！");
        }
        Long userId = currentUserId();
        usersService.saveUserActiveLog(userId, "-");
        LatestVersionDO latestVersion = latestVersionService.find("postgraduate-" + app);
        latestVersion.setApp(app);
        if (latestVersion.getLatestVersion().equals(version)) {
            return BusinessResult.createSuccessInstance(null);
        }

        LatestVersionVO versionVO = LatestVersionVO.from(latestVersion);
        if (Objects.isNull(versionVO) || compareVersion(versionVO.getLatestVersion(), version) < 0) {
            return BusinessResult.createSuccessInstance(null);
        }
        versionVO.setForceUpdate(compareVersion(version, versionVO.getMinSupportVersion()) < 0);
        TipVO tip = TipVO.builder().ext(versionVO).type(TipVO.TipType.VERSION_UPDATE.name()).build();

        if (versionVO.getForceUpdate()) {
            return BusinessResult.createSuccessInstance(tip);
        }

        if (Objects.isNull(userId)) {
            return BusinessResult.createSuccessInstance(null);
        }
        Boolean hasKey = redisTemplate.hasKey(getPostgraduateKey(userId, app, versionVO.getLatestVersion()));
        if (hasKey) {
            String existVersion = redisTemplate.opsForValue().get(getPostgraduateKey(userId, app, versionVO.getLatestVersion()));
            if (StringUtils.isNotBlank(existVersion) && compareVersion(existVersion, version) < 0) {
                putPostgraduate(userId, app, version, versionVO.getLatestVersion());
                return BusinessResult.createSuccessInstance(tip);
            }
            return BusinessResult.createSuccessInstance(null);
        }
        putPostgraduate(userId, app, version, versionVO.getLatestVersion());
        return BusinessResult.createSuccessInstance(tip);
    }

    @AutoLog
    @ApiOperation(value = "开放日弹窗广告(1、弹 0、不弹)", notes = "开放日弹窗广告")
    @GetMapping("/study/openDay/advert/alert")
    @AuthSig
    public BusinessResult<OpenDayAdvertVO> studyOpenDayAlertAdvert() {

        Long userId = currentUserId();
        Long code = dicService.findByCode(STUDY_OPEN_DAYS_ALERT_ADVERT.dicName()).filter(STUDY_OPEN_DAYS_ALERT_ADVERT.alert_switch.name()).getCode();
        //广告开关校验，关闭开关直接返回
        if (YN.NO.getCode().equals(code)) {
            return BusinessResult.createSuccessInstance(NO_OPEN_DAY_ADVERT);
        }

        String[] days = dicService.findByCode(STUDY_OPEN_DAYS.dicName()).filter(STUDY_OPEN_DAYS.days.name()).getData().split(",");
        DicDO alertAdvertDic = dicService.findByCode(STUDY_OPEN_DAYS_ALERT_ADVERT.dicName());
        String url = alertAdvertDic.filter(STUDY_OPEN_DAYS_ALERT_ADVERT.url.name()).getData(),
                title = alertAdvertDic.filter(STUDY_OPEN_DAYS_ALERT_ADVERT.title.name()).getData();
        List<Date> studyDays = Arrays.stream(days).map(DateUtil::strToDate).collect(Collectors.toList());

        OpenDayAdvertVO advert = OpenDayAdvertVO.builder()
                .code(YN.YES.getCode())
                .url(url)
                .title(title)
                .build();

        //没有设置开放日，直接返回
        if (studyDays.isEmpty()) {
            return BusinessResult.createSuccessInstance(NO_OPEN_DAY_ADVERT);
        }
        Date today = DateUtil.strToDate(DateUtil.getStringDateShort());
        Date date = studyDays.stream().filter(day -> {
            long l = day.getTime() - today.getTime();
            return l >= 0 && l / 1000 / 60 / 60 / 24 <= 2;
        }).findFirst().orElse(null);

        //是否为某个开放日前两天或当天，不是的话直接返回
        if (Objects.isNull(date)) {
            return BusinessResult.createSuccessInstance(NO_OPEN_DAY_ADVERT);
        }

        //如果是游客，开放日前两天直接弹出
        if (Objects.isNull(userId)) {
            return BusinessResult.createSuccessInstance(advert);
        }

        String record = redisTemplate.opsForValue().get(getStudyDayKey(userId));
        //如果当天已经弹出过，不再弹出
        if (Objects.isNull(record)) {
            redisTemplate.opsForValue().set(getStudyDayKey(userId), "", 24, TimeUnit.DAYS);
            return BusinessResult.createSuccessInstance(advert);
        }
        return BusinessResult.createSuccessInstance(NO_OPEN_DAY_ADVERT);
    }

    private OpenDayAdvertVO NO_OPEN_DAY_ADVERT = OpenDayAdvertVO.builder().code(YN.NO.getCode()).build();

    /**
     * 版本号比较
     *
     * @param v1
     * @param v2
     * @return 0代表相等，1代表左边大，-1代表右边大
     * Utils.compareVersion("1.0.358_20180820090554","1.0.358_20180820090553")=1
     */
    private static int compareVersion(String v1, String v2) {
        if (v1.equals(v2)) {
            return 0;
        }
        String[] version1Array = v1.split("[._]");
        String[] version2Array = v2.split("[._]");
        int index = 0;
        int minLen = Math.min(version1Array.length, version2Array.length);
        long diff = 0;

        while (index < minLen
                && (diff = Long.parseLong(version1Array[index])
                - Long.parseLong(version2Array[index])) == 0) {
            index++;
        }
        if (diff == 0) {
            for (int i = index; i < version1Array.length; i++) {
                if (Long.parseLong(version1Array[i]) > 0) {
                    return 1;
                }
            }

            for (int i = index; i < version2Array.length; i++) {
                if (Long.parseLong(version2Array[i]) > 0) {
                    return -1;
                }
            }
            return 0;
        } else {
            return diff > 0 ? 1 : -1;
        }
    }

    private void put(Long userId, String app, String version, String latestVersion) {
        redisTemplate.opsForValue().set(getKey(userId, app, latestVersion), version);
    }

    public static final int TIME_OUT = 7;

    private String getKey(Long userId, String app, String latestVersion) {
        return USERS_BY_VERSION_TIPS + "_" + app + "_" + latestVersion + userId;
    }


    private void putPostgraduate(Long userId, String app, String version, String latestVersion) {
        redisTemplate.opsForValue().set(getPostgraduateKey(userId, app, latestVersion), version);
    }

    private String getPostgraduateKey(Long userId, String app, String latestVersion) {
        return POSTGRADUATE_USERS_BY_VERSION_TIPS + "_" + app + "_" + latestVersion + userId;
    }

    private String getStudyDayKey(Long userId) {
        String today = DateUtil.getTodayShort();
        return STUDY_OPEN_DAY_ALERT_ADVERT + "_" + today + "_" + userId;
    }

    @AutoLog
    @ApiOperation(value = "APP错误信息上报", notes = "APP错误信息上报")
    @PostMapping("/app/error")
    @AuthSig
    public BusinessResult report(HttpServletRequest request, @RequestBody AppErrorMessage message) {
        AppSystemMessageDO messageDO = AppSystemMessageDO.from(message);

        try {
            String headerString = objectMapper.writeValueAsString(AutoLogAspect.getHeaders(request));
            messageDO.setRequestHeader(headerString);
        } catch (JsonProcessingException e) {
            log.error("", e);
        }

        if (Objects.isNull(messageDO) || Objects.isNull(messageDO.getMsg())) {
            log.error("messageDO = {}", messageDO);
            return BusinessResult.createSuccessInstance(null);
        }
        messageDO.setUserId(currentUserId());
        commonTaskExecutor.submit(() -> appSystemMessageService.save(messageDO));
        return BusinessResult.createSuccessInstance(null);
    }

    @Autowired
    private LatestVersionServiceImpl latestVersionService;
    @Autowired
    @Lazy
    private UsersServiceImpl usersService;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private AppSystemMessageServiceImpl appSystemMessageService;
    @Resource(name = "commonTaskExecutor")
    private AsyncTaskExecutor commonTaskExecutor;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private UserCardsServiceImpl userCardsService;
    @Autowired
    private DicServiceImpl dicService;
}
